home *** CD-ROM | disk | FTP | other *** search
/ Nothing but Tetris / Nothing but Tetris.iso / amiga / yactris / src / dbug / dbug.c next >
C/C++ Source or Header  |  1994-01-01  |  45KB  |  1,823 lines

  1. /******************************************************************************
  2.  *                                          *
  3.  *                   N O T I C E                      *
  4.  *                                          *
  5.  *              Copyright Abandoned, 1987, Fred Fish              *
  6.  *                                          *
  7.  *                                          *
  8.  *    This previously copyrighted work has been placed into the  public     *
  9.  *    domain    by  the  author  and  may be freely used for any purpose,     *
  10.  *    private or commercial.                              *
  11.  *                                          *
  12.  *    Because of the number of inquiries I was receiving about the  use     *
  13.  *    of this product in commercially developed works I have decided to     *
  14.  *    simply make it public domain to further its unrestricted use.    I     *
  15.  *    specifically  would  be  most happy to see this material become a     *
  16.  *    part of the standard Unix distributions by AT&T and the  Berkeley     *
  17.  *    Computer  Science  Research Group, and a standard part of the GNU     *
  18.  *    system from the Free Software Foundation.                  *
  19.  *                                          *
  20.  *    I would appreciate it, as a courtesy, if this notice is  left  in     *
  21.  *    all copies and derivative works.  Thank you.                  *
  22.  *                                          *
  23.  *    The author makes no warranty of any kind  with    respect  to  this     *
  24.  *    product  and  explicitly disclaims any implied warranties of mer-     *
  25.  *    chantability or fitness for any particular purpose.              *
  26.  *                                          *
  27.  ******************************************************************************
  28.  */
  29.  
  30. /*  Small modifications for DICE made by Jonathan Springer, 6 January 1993.
  31. */
  32.  
  33. /*
  34.  *  FILE
  35.  *
  36.  *    dbug.c     runtime support routines for dbug package
  37.  *
  38.  *  SCCS
  39.  *
  40.  *    @(#)dbug.c      1.25    7/25/89
  41.  *
  42.  *  DESCRIPTION
  43.  *
  44.  *    These are the runtime support routines for the dbug package.
  45.  *    The dbug package has two main components; the user include
  46.  *    file containing various macro definitions, and the runtime
  47.  *    support routines which are called from the macro expansions.
  48.  *
  49.  *    Externally visible functions in the runtime support module
  50.  *    use the naming convention pattern "_db_xx...xx_", thus
  51.  *    they are unlikely to collide with user defined function names.
  52.  *
  53.  *  AUTHOR(S)
  54.  *
  55.  *    Fred Fish        (base code)
  56.  *    Enhanced Software Technologies, Tempe, AZ
  57.  *    asuvax!mcdphx!estinc!fnf
  58.  *
  59.  *    Binayak Banerjee    (profiling enhancements)
  60.  *    seismo!bpa!sjuvax!bbanerje
  61.  */
  62.  
  63. #include <stdio.h>
  64.  
  65. #if NO_VARARGS
  66. #include "vargs.h"              /* Use our "fake" varargs */
  67. #else
  68. #include <varargs.h>        /* Use system supplied varargs package */
  69. #endif
  70.  
  71. #ifdef AMIGA
  72. #define HZ (50)                 /* Probably in some header somewhere */
  73. #endif
  74.  
  75. #ifdef M_XENIX            /* Some xenix compilers predefine this */
  76. #ifndef xenix
  77. #define xenix 1
  78. #endif
  79. #endif
  80.  
  81. /*
  82.  *    Manifest constants that should not require any changes.
  83.  */
  84.  
  85. #define FALSE        0    /* Boolean FALSE */
  86. #define TRUE        1    /* Boolean TRUE */
  87. #define EOS        '\000'  /* End Of String marker */
  88.  
  89. /*
  90.  *    Manifest constants which may be "tuned" if desired.
  91.  */
  92.  
  93. #define PRINTBUF    1024    /* Print buffer size */
  94. #define INDENT        4    /* Indentation per trace level */
  95. #define MAXDEPTH    200    /* Maximum trace depth default */
  96.  
  97. /*
  98.  *    The following flags are used to determine which
  99.  *    capabilities the user has enabled with the state
  100.  *    push macro.
  101.  */
  102.  
  103. #define TRACE_ON    000001    /* Trace enabled */
  104. #define DEBUG_ON    000002    /* Debug enabled */
  105. #define FILE_ON     000004    /* File name print enabled */
  106. #define LINE_ON     000010    /* Line number print enabled */
  107. #define DEPTH_ON    000020    /* Function nest level print enabled */
  108. #define PROCESS_ON    000040    /* Process name print enabled */
  109. #define NUMBER_ON    000100    /* Number each line of output */
  110. #define PROFILE_ON    000200    /* Print out profiling code */
  111. #define PID_ON        000400    /* Identify each line with process id */
  112.  
  113. #define TRACING (stack -> flags & TRACE_ON)
  114. #define DEBUGGING (stack -> flags & DEBUG_ON)
  115. #define PROFILING (stack -> flags & PROFILE_ON)
  116. #define STREQ(a,b) (strcmp(a,b) == 0)
  117.  
  118. /*
  119.  *    Typedefs to make things more obvious.
  120.  */
  121.  
  122. #define VOID void        /* Can't use typedef for most compilers */
  123. typedef int BOOLEAN;
  124.  
  125. /*
  126.  *    Make it easy to change storage classes if necessary.
  127.  */
  128.  
  129. #define LOCAL static        /* Names not needed by outside world */
  130. #define IMPORT extern        /* Names defined externally */
  131. #define EXPORT            /* Allocated here, available globally */
  132. #define AUTO auto        /* Names to be allocated on stack */
  133. #define REGISTER register    /* Names to be placed in registers */
  134.  
  135. /*
  136.  * The default file for profiling.  Could also add another flag
  137.  * (G?) which allowed the user to specify this.
  138.  *
  139.  * If the automatic variables get allocated on the stack in
  140.  * reverse order from their declarations, then define AUTOS_REVERSE.
  141.  * This is used by the code that keeps track of stack usage.  For
  142.  * forward allocation, the difference in the dbug frame pointers
  143.  * represents stack used by the callee function.  For reverse allocation,
  144.  * the difference represents stack used by the caller function.
  145.  *
  146.  */
  147.  
  148. #define PROF_FILE    "dbugmon.out"
  149. #define PROF_EFMT    "E\t%ld\t%s\n"
  150. #define PROF_SFMT    "S\t%lx\t%lx\t%s\n"
  151. #define PROF_XFMT    "X\t%ld\t%s\n"
  152.  
  153. #if M_I386        /* predefined by xenix 386 compiler */
  154. #define AUTOS_REVERSE 1
  155. #endif
  156.  
  157. /*
  158.  *    Variables which are available externally but should only
  159.  *    be accessed via the macro package facilities.
  160.  */
  161.  
  162. EXPORT FILE *_db_fp_ = stderr;        /* Output stream, default stderr */
  163. EXPORT FILE *_db_pfp_ = (FILE *)0;      /* Profile stream, 'dbugmon.out' */
  164. EXPORT char *_db_process_ = "dbug";     /* Pointer to process name; argv[0] */
  165. EXPORT BOOLEAN _db_on_ = FALSE;     /* TRUE if debugging currently on */
  166. EXPORT BOOLEAN _db_pon_ = FALSE;    /* TRUE if debugging currently on */
  167.  
  168. /*
  169.  *    Externally supplied functions.
  170.  */
  171.  
  172. #if (unix || xenix)             /* Only needed for unix */
  173. IMPORT VOID perror ();          /* Print system/library error */
  174. IMPORT int chown ();            /* Change owner of a file */
  175. IMPORT int getgid ();           /* Get real group id */
  176. IMPORT int getuid ();           /* Get real user id */
  177. IMPORT int access ();           /* Test file for access */
  178. #else
  179. #if !(AMIGA && (LATTICE || _DCC))
  180. LOCAL VOID perror ();           /* Fake system/library error print routine */
  181. #endif
  182. #endif
  183.  
  184. # if BSD4_3 || sun
  185. IMPORT int getrusage ();
  186. #endif
  187.  
  188. IMPORT int atoi ();             /* Convert ascii to integer */
  189. IMPORT VOID exit ();            /* Terminate execution */
  190. /*IMPORT int fclose (); */          /* Close a stream */
  191. /*IMPORT FILE *fopen (); */         /* Open a stream */
  192. /*IMPORT int fprintf (); */         /* Formatted print on file */
  193. IMPORT VOID free ();
  194. IMPORT char *malloc ();         /* Allocate memory */
  195. IMPORT int strcmp ();           /* Compare strings */
  196. IMPORT char *strcpy ();         /* Copy strings around */
  197. IMPORT int strlen ();           /* Find length of string */
  198.  
  199. #if NO_VARARGS
  200. static int vfprintf ();         /* Use our local fake version */
  201. #else
  202. /*IMPORT int vfprintf ();*/         /* Varargs form of fprintf */
  203. #endif
  204.  
  205. #ifndef fflush            /* This is sometimes a macro */
  206. /*IMPORT int fflush ();*/           /* Flush output for stream */
  207. #endif
  208.  
  209. /*
  210.  *    The user may specify a list of functions to trace or
  211.  *    debug.    These lists are kept in a linear linked list,
  212.  *    a very simple implementation.
  213.  */
  214.  
  215. struct link {
  216.     char *string;        /* Pointer to link's contents */
  217.     struct link *next_link;    /* Pointer to the next link */
  218. };
  219.  
  220. /*
  221.  *    Debugging states can be pushed or popped off of a
  222.  *    stack which is implemented as a linked list.  Note
  223.  *    that the head of the list is the current state and the
  224.  *    stack is pushed by adding a new state to the head of the
  225.  *    list or popped by removing the first link.
  226.  */
  227.  
  228. struct state {
  229.     int flags;                /* Current state flags */
  230.     int maxdepth;            /* Current maximum trace depth */
  231.     unsigned int delay;         /* Delay after each output line */
  232.     int level;                /* Current function nesting level */
  233.     FILE *out_file;            /* Current output stream */
  234.     FILE *prof_file;            /* Current profiling stream */
  235.     struct link *functions;        /* List of functions */
  236.     struct link *p_functions;        /* List of profiled functions */
  237.     struct link *keywords;        /* List of debug keywords */
  238.     struct link *processes;        /* List of process names */
  239.     struct state *next_state;        /* Next state in the list */
  240. };
  241.  
  242. LOCAL struct state *stack = NULL;    /* Linked list of stacked states */
  243.  
  244. /*
  245.  *    Local variables not seen by user.
  246.  */
  247.  
  248. LOCAL int lineno = 0;        /* Current debugger output line number */
  249. LOCAL char *func = "?func";     /* Name of current user function */
  250. LOCAL char *file = "?file";     /* Name of current user file */
  251. LOCAL BOOLEAN init_done = FALSE;/* Set to TRUE when initialization done */
  252. LOCAL char **framep = NULL;    /* Pointer to current frame */
  253.  
  254. #if (unix || xenix || AMIGA)
  255. LOCAL int jmplevel;        /* Remember nesting level at setjmp () */
  256. LOCAL char *jmpfunc;        /* Remember current function for setjmp */
  257. LOCAL char *jmpfile;        /* Remember current file for setjmp */
  258. #endif
  259.  
  260. LOCAL struct link *ListParse ();/* Parse a debug command string */
  261. LOCAL char *StrDup ();          /* Make a fresh copy of a string */
  262. LOCAL VOID OpenFile ();         /* Open debug output stream */
  263. LOCAL VOID OpenProfile ();      /* Open profile output stream */
  264. LOCAL VOID CloseFile ();        /* Close debug output stream */
  265. LOCAL VOID PushState ();        /* Push current debug state */
  266. LOCAL VOID ChangeOwner ();      /* Change file owner and group */
  267. LOCAL BOOLEAN DoTrace ();       /* Test for tracing enabled */
  268. LOCAL BOOLEAN Writable ();      /* Test to see if file is writable */
  269. LOCAL unsigned long Clock ();   /* Return current user time (ms) */
  270. LOCAL long *DbugMalloc ();      /* Allocate memory for runtime support */
  271. LOCAL char *BaseName ();        /* Remove leading pathname components */
  272. LOCAL VOID DoPrefix ();         /* Print debugger line prefix */
  273. LOCAL VOID FreeList ();         /* Free memory from linked list */
  274. LOCAL VOID Indent ();           /* Indent line to specified indent */
  275.  
  276.                 /* Supplied in Sys V runtime environ */
  277. LOCAL char *strtok ();          /* Break string into tokens */
  278. LOCAL char *strrchr ();         /* Find last occurance of char */
  279.  
  280. /*
  281.  *    The following local variables are used to hold the state information
  282.  *    between the call to _db_pargs_() and (), during
  283.  *    expansion of the DBUG_PRINT macro.  This is the only macro
  284.  *    that currently uses these variables.
  285.  *
  286.  *    These variables are currently used only by _db_pargs_() and
  287.  *    _db_doprnt_().
  288.  */
  289.  
  290. LOCAL int u_line = 0;        /* User source code line number */
  291. LOCAL char *u_keyword = "?";    /* Keyword for current macro */
  292.  
  293. /*
  294.  *    Miscellaneous printf format strings.
  295.  */
  296.  
  297. #define ERR_MISSING_RETURN "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n"
  298. #define ERR_OPEN "%s: can't open debug output stream \"%s\": "
  299. #define ERR_CLOSE "%s: can't close debug file: "
  300. #define ERR_ABORT "%s: debugger aborting because %s\n"
  301. #define ERR_CHOWN "%s: can't change owner/group of \"%s\": "
  302.  
  303. /*
  304.  *    Macros and defines for testing file accessibility under UNIX.
  305.  */
  306.  
  307. #if (unix || xenix)
  308. #  define A_EXISTS    00        /* Test for file existance */
  309. #  define A_EXECUTE    01        /* Test for execute permission */
  310. #  define A_WRITE    02        /* Test for write access */
  311. #  define A_READ    03        /* Test for read access */
  312. #  define EXISTS(pathname) (access (pathname, A_EXISTS) == 0)
  313. #  define WRITABLE(pathname) (access (pathname, A_WRITE) == 0)
  314. #else
  315. #  define EXISTS(pathname) (FALSE)      /* Assume no existance */
  316. #endif
  317.  
  318. /*
  319.  *    Translate some calls among different systems.
  320.  */
  321.  
  322. #if (unix || xenix)
  323. # define Delay sleep
  324. IMPORT unsigned int sleep ();   /* Pause for given number of seconds */
  325. #endif
  326.  
  327. #if AMIGA
  328. IMPORT int Delay ();            /* Pause for given number of ticks */
  329. #endif
  330.  
  331. /*
  332.  *  FUNCTION
  333.  *
  334.  *    _db_push_    push current debugger state and set up new one
  335.  *
  336.  *  SYNOPSIS
  337.  *
  338.  *    VOID _db_push_ (control)
  339.  *    char *control;
  340.  *
  341.  *  DESCRIPTION
  342.  *
  343.  *    Given pointer to a debug control string in "control", pushes
  344.  *    the current debug state, parses the control string, and sets
  345.  *    up a new debug state.
  346.  *
  347.  *    The only attribute of the new state inherited from the previous
  348.  *    state is the current function nesting level.  This can be
  349.  *    overridden by using the "r" flag in the control string.
  350.  *
  351.  *    The debug control string is a sequence of colon separated fields
  352.  *    as follows:
  353.  *
  354.  *        <field_1>:<field_2>:...:<field_N>
  355.  *
  356.  *    Each field consists of a mandatory flag character followed by
  357.  *    an optional "," and comma separated list of modifiers:
  358.  *
  359.  *        flag[,modifier,modifier,...,modifier]
  360.  *
  361.  *    The currently recognized flag characters are:
  362.  *
  363.  *        d    Enable output from DBUG_<N> macros for
  364.  *            for the current state.    May be followed
  365.  *            by a list of keywords which selects output
  366.  *            only for the DBUG macros with that keyword.
  367.  *            A null list of keywords implies output for
  368.  *            all macros.
  369.  *
  370.  *        D    Delay after each debugger output line.
  371.  *            The argument is the number of tenths of seconds
  372.  *            to delay, subject to machine capabilities.
  373.  *            I.E.  -#D,20 is delay two seconds.
  374.  *
  375.  *        f    Limit debugging and/or tracing, and profiling to the
  376.  *            list of named functions.  Note that a null list will
  377.  *            disable all functions.    The appropriate "d" or "t"
  378.  *            flags must still be given, this flag only limits their
  379.  *            actions if they are enabled.
  380.  *
  381.  *        F    Identify the source file name for each
  382.  *            line of debug or trace output.
  383.  *
  384.  *        i    Identify the process with the pid for each line of
  385.  *            debug or trace output.
  386.  *
  387.  *        g    Enable profiling.  Create a file called 'dbugmon.out'
  388.  *            containing information that can be used to profile
  389.  *            the program.  May be followed by a list of keywords
  390.  *            that select profiling only for the functions in that
  391.  *            list.  A null list implies that all functions are
  392.  *            considered.
  393.  *
  394.  *        L    Identify the source file line number for
  395.  *            each line of debug or trace output.
  396.  *
  397.  *        n    Print the current function nesting depth for
  398.  *            each line of debug or trace output.
  399.  *
  400.  *        N    Number each line of dbug output.
  401.  *
  402.  *        o    Redirect the debugger output stream to the
  403.  *            specified file.  The default output is stderr.
  404.  *
  405.  *        p    Limit debugger actions to specified processes.
  406.  *            A process must be identified with the
  407.  *            DBUG_PROCESS macro and match one in the list
  408.  *            for debugger actions to occur.
  409.  *
  410.  *        P    Print the current process name for each
  411.  *            line of debug or trace output.
  412.  *
  413.  *        r    When pushing a new state, do not inherit
  414.  *            the previous state's function nesting level.
  415.  *            Useful when the output is to start at the
  416.  *            left margin.
  417.  *
  418.  *        t    Enable function call/exit trace lines.
  419.  *            May be followed by a list (containing only
  420.  *            one modifier) giving a numeric maximum
  421.  *            trace level, beyond which no output will
  422.  *            occur for either debugging or tracing
  423.  *            macros.  The default is a compile time
  424.  *            option.
  425.  *
  426.  *    Some examples of debug control strings which might appear
  427.  *    on a shell command line (the "-#" is typically used to
  428.  *    introduce a control string to an application program) are:
  429.  *
  430.  *        -#d:t
  431.  *        -#d:f,main,subr1:F:L:t,20
  432.  *        -#d,input,output,files:n
  433.  *
  434.  *    For convenience, any leading "-#" is stripped off.
  435.  *
  436.  */
  437.  
  438. VOID _db_push_ (control)
  439. char *control;
  440. {
  441.     REGISTER char *scan;
  442.     REGISTER struct link *temp;
  443.  
  444.     if (control && *control == '-') {
  445.     if (*++control == '#') {
  446.         control++;
  447.     }
  448.     }
  449.     control = StrDup (control);
  450.     PushState ();
  451.     scan = strtok (control, ":");
  452.     for (; scan != NULL; scan = strtok ((char *)NULL, ":")) {
  453.     switch (*scan++) {
  454.         case 'd':
  455.         _db_on_ = TRUE;
  456.         stack -> flags |= DEBUG_ON;
  457.         if (*scan++ == ',') {
  458.             stack -> keywords = ListParse (scan);
  459.         }
  460.         break;
  461.         case 'D':
  462.         stack -> delay = 0;
  463.         if (*scan++ == ',') {
  464.             temp = ListParse (scan);
  465.             stack -> delay = DelayArg (atoi (temp -> string));
  466.             FreeList (temp);
  467.         }
  468.         break;
  469.         case 'f':
  470.         if (*scan++ == ',') {
  471.             stack -> functions = ListParse (scan);
  472.         }
  473.         break;
  474.         case 'F':
  475.         stack -> flags |= FILE_ON;
  476.         break;
  477.         case 'i':
  478.         stack -> flags |= PID_ON;
  479.         break;
  480.         case 'g':
  481.         _db_pon_ = TRUE;
  482.         OpenProfile(PROF_FILE);
  483.         stack -> flags |= PROFILE_ON;
  484.         if (*scan++ == ',') {
  485.             stack -> p_functions = ListParse (scan);
  486.         }
  487.         break;
  488.         case 'L':
  489.         stack -> flags |= LINE_ON;
  490.         break;
  491.         case 'n':
  492.         stack -> flags |= DEPTH_ON;
  493.         break;
  494.         case 'N':
  495.         stack -> flags |= NUMBER_ON;
  496.         break;
  497.         case 'o':
  498.         if (*scan++ == ',') {
  499.             temp = ListParse (scan);
  500.             OpenFile (temp -> string);
  501.             FreeList (temp);
  502.         } else {
  503.             OpenFile ("-");
  504.         }
  505.         break;
  506.         case 'p':
  507.         if (*scan++ == ',') {
  508.             stack -> processes = ListParse (scan);
  509.         }
  510.         break;
  511.         case 'P':
  512.         stack -> flags |= PROCESS_ON;
  513.         break;
  514.         case 'r':
  515.         stack -> level = 0;
  516.         break;
  517.         case 't':
  518.         stack -> flags |= TRACE_ON;
  519.         if (*scan++ == ',') {
  520.             temp = ListParse (scan);
  521.             stack -> maxdepth = atoi (temp -> string);
  522.             FreeList (temp);
  523.         }
  524.         break;
  525.     }
  526.     }
  527.     free (control);
  528. }
  529.  
  530. /*
  531.  *  FUNCTION
  532.  *
  533.  *    _db_pop_    pop the debug stack
  534.  *
  535.  *  DESCRIPTION
  536.  *
  537.  *    Pops the debug stack, returning the debug state to its
  538.  *    condition prior to the most recent _db_push_ invocation.
  539.  *    Note that the pop will fail if it would remove the last
  540.  *    valid state from the stack.  This prevents user errors
  541.  *    in the push/pop sequence from screwing up the debugger.
  542.  *    Maybe there should be some kind of warning printed if the
  543.  *    user tries to pop too many states.
  544.  *
  545.  */
  546.  
  547. VOID _db_pop_ ()
  548. {
  549.     REGISTER struct state *discard;
  550.  
  551.     discard = stack;
  552.     if (discard != NULL && discard -> next_state != NULL) {
  553.     stack = discard -> next_state;
  554.     _db_fp_ = stack -> out_file;
  555.     _db_pfp_ = stack -> prof_file;
  556.     if (discard -> keywords != NULL) {
  557.         FreeList (discard -> keywords);
  558.     }
  559.     if (discard -> functions != NULL) {
  560.         FreeList (discard -> functions);
  561.     }
  562.     if (discard -> processes != NULL) {
  563.         FreeList (discard -> processes);
  564.     }
  565.     if (discard -> p_functions != NULL) {
  566.         FreeList (discard -> p_functions);
  567.     }
  568.     CloseFile (discard -> out_file);
  569.     CloseFile (discard -> prof_file);
  570.     free ((char *) discard);
  571.     }
  572. }
  573.  
  574. /*
  575.  *  FUNCTION
  576.  *
  577.  *    _db_enter_    process entry point to user function
  578.  *
  579.  *  SYNOPSIS
  580.  *
  581.  *    VOID _db_enter_ (_func_, _file_, _line_,
  582.  *             _sfunc_, _sfile_, _slevel_, _sframep_)
  583.  *    char *_func_;        points to current function name
  584.  *    char *_file_;        points to current file name
  585.  *    int _line_;        called from source line number
  586.  *    char **_sfunc_;     save previous _func_
  587.  *    char **_sfile_;     save previous _file_
  588.  *    int *_slevel_;        save previous nesting level
  589.  *    char ***_sframep_;    save previous frame pointer
  590.  *
  591.  *  DESCRIPTION
  592.  *
  593.  *    Called at the beginning of each user function to tell
  594.  *    the debugger that a new function has been entered.
  595.  *    Note that the pointers to the previous user function
  596.  *    name and previous user file name are stored on the
  597.  *    caller's stack (this is why the ENTER macro must be
  598.  *    the first "executable" code in a function, since it
  599.  *    allocates these storage locations).  The previous nesting
  600.  *    level is also stored on the callers stack for internal
  601.  *    self consistency checks.
  602.  *
  603.  *    Also prints a trace line if tracing is enabled and
  604.  *    increments the current function nesting depth.
  605.  *
  606.  *    Note that this mechanism allows the debugger to know
  607.  *    what the current user function is at all times, without
  608.  *    maintaining an internal stack for the function names.
  609.  *
  610.  */
  611.  
  612. VOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_,
  613.          _sframep_)
  614. char *_func_;
  615. char *_file_;
  616. int _line_;
  617. char **_sfunc_;
  618. char **_sfile_;
  619. int *_slevel_;
  620. char ***_sframep_;
  621. {
  622.     long stackused;
  623.  
  624.     if (!init_done) {
  625.     _db_push_ ("");
  626.     }
  627.     *_sfunc_ = func;
  628.     func = _func_;
  629.     *_sfile_ = file;
  630.     file = BaseName (_file_);
  631.     stack -> level++;
  632.     *_slevel_ = stack -> level;
  633.     *_sframep_ = framep;
  634.     framep = (char **) _sframep_;
  635.     if (DoProfile ()) {
  636.     if (*framep == NULL) {
  637.         stackused = 0;
  638.     } else {
  639.         stackused = ((long)(*framep)) - ((long)(framep));
  640.         stackused = stackused > 0 ? stackused : -stackused;
  641.     }
  642.     (VOID) fprintf (_db_pfp_, PROF_EFMT , Clock (), func);
  643. #if AUTOS_REVERSE
  644.     (VOID) fprintf (_db_pfp_, PROF_SFMT, framep, stackused, *_sfunc_);
  645. #else
  646.     (VOID) fprintf (_db_pfp_, PROF_SFMT, framep, stackused, func);
  647. #endif
  648.     (VOID) fflush (_db_pfp_);
  649.     }
  650.     if (DoTrace ()) {
  651.     DoPrefix (_line_);
  652.     Indent (stack -> level);
  653.     (VOID) fprintf (_db_fp_, ">%s\n", func);
  654.     (VOID) fflush (_db_fp_);
  655.     (VOID) Delay (stack -> delay);
  656.     }
  657. }
  658.  
  659. /*
  660.  *  FUNCTION
  661.  *
  662.  *    _db_return_    process exit from user function
  663.  *
  664.  *  SYNOPSIS
  665.  *
  666.  *    VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
  667.  *    int _line_;        current source line number
  668.  *    char **_sfunc_;     where previous _func_ is to be retrieved
  669.  *    char **_sfile_;     where previous _file_ is to be retrieved
  670.  *    int *_slevel_;        where previous level was stashed
  671.  *
  672.  *  DESCRIPTION
  673.  *
  674.  *    Called just before user function executes an explicit or implicit
  675.  *    return.  Prints a trace line if trace is enabled, decrements
  676.  *    the current nesting level, and restores the current function and
  677.  *    file names from the defunct function's stack.
  678.  *
  679.  */
  680.  
  681. VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
  682. int _line_;
  683. char **_sfunc_;
  684. char **_sfile_;
  685. int *_slevel_;
  686. {
  687.     if (!init_done) {
  688.     _db_push_ ("");
  689.     }
  690.     if (stack -> level != *_slevel_ && (TRACING || DEBUGGING || PROFILING)) {
  691.     (VOID) fprintf (_db_fp_, ERR_MISSING_RETURN, _db_process_, func);
  692.     } else if (DoProfile ()) {
  693.     (VOID) fprintf (_db_pfp_, PROF_XFMT, Clock(), func);
  694.     } else if (DoTrace ()) {
  695.     DoPrefix (_line_);
  696.     Indent (stack -> level);
  697.     (VOID) fprintf (_db_fp_, "<%s\n", func);
  698.     }
  699.     (VOID) fflush (_db_fp_);
  700.     (VOID) Delay (stack -> delay);
  701.     stack -> level = *_slevel_ - 1;
  702.     func = *_sfunc_;
  703.     file = *_sfile_;
  704.     if (framep != NULL) {
  705.     framep = (char **) *framep;
  706.     }
  707. }
  708.  
  709. /*
  710.  *  FUNCTION
  711.  *
  712.  *    _db_pargs_    log arguments for subsequent use by _db_doprnt_()
  713.  *
  714.  *  SYNOPSIS
  715.  *
  716.  *    VOID _db_pargs_ (_line_, keyword)
  717.  *    int _line_;
  718.  *    char *keyword;
  719.  *
  720.  *  DESCRIPTION
  721.  *
  722.  *    The new universal printing macro DBUG_PRINT, which replaces
  723.  *    all forms of the DBUG_N macros, needs two calls to runtime
  724.  *    support routines.  The first, this function, remembers arguments
  725.  *    that are used by the subsequent call to _db_doprnt_().
  726.  *
  727.  */
  728.  
  729. VOID _db_pargs_ (_line_, keyword)
  730. int _line_;
  731. char *keyword;
  732. {
  733.     u_line = _line_;
  734.     u_keyword = keyword;
  735. }
  736.  
  737. /*
  738.  *  FUNCTION
  739.  *
  740.  *    _db_doprnt_    handle print of debug lines
  741.  *
  742.  *  SYNOPSIS
  743.  *
  744.  *    VOID _db_doprnt_ (format, va_alist)
  745.  *    char *format;
  746.  *    va_dcl;
  747.  *
  748.  *  DESCRIPTION
  749.  *
  750.  *    When invoked via one of the DBUG macros, tests the current keyword
  751.  *    set by calling _db_pargs_() to see if that macro has been selected
  752.  *    for processing via the debugger control string, and if so, handles
  753.  *    printing of the arguments via the format string.  The line number
  754.  *    of the DBUG macro in the source is found in u_line.
  755.  *
  756.  *    Note that the format string SHOULD NOT include a terminating
  757.  *    newline, this is supplied automatically.
  758.  *
  759.  */
  760.  
  761. /*VARARGS1*/
  762. VOID _db_doprnt_ (format, va_alist)
  763. char *format;
  764. va_dcl
  765. {
  766.     va_list args;
  767.  
  768.     va_start (args);
  769.     if (_db_keyword_ (u_keyword)) {
  770.     DoPrefix (u_line);
  771.     if (TRACING) {
  772.         Indent (stack -> level + 1);
  773.     } else {
  774.         (VOID) fprintf (_db_fp_, "%s: ", func);
  775.     }
  776.     (VOID) fprintf (_db_fp_, "%s: ", u_keyword);
  777.     (VOID) vfprintf (_db_fp_, format, args);
  778.     (VOID) fprintf (_db_fp_, "\n");
  779.     (VOID) fflush (_db_fp_);
  780.     (VOID) Delay (stack -> delay);
  781.     }
  782.     va_end (args);
  783. }
  784.  
  785. /*
  786.  *  FUNCTION
  787.  *
  788.  *    ListParse    parse list of modifiers in debug control string
  789.  *
  790.  *  SYNOPSIS
  791.  *
  792.  *    LOCAL struct link *ListParse (ctlp)
  793.  *    char *ctlp;
  794.  *
  795.  *  DESCRIPTION
  796.  *
  797.  *    Given pointer to a comma separated list of strings in "cltp",
  798.  *    parses the list, building a list and returning a pointer to it.
  799.  *    The original comma separated list is destroyed in the process of
  800.  *    building the linked list, thus it had better be a duplicate
  801.  *    if it is important.
  802.  *
  803.  *    Note that since each link is added at the head of the list,
  804.  *    the final list will be in "reverse order", which is not
  805.  *    significant for our usage here.
  806.  *
  807.  */
  808.  
  809. LOCAL struct link *ListParse (ctlp)
  810. char *ctlp;
  811. {
  812.     REGISTER char *start;
  813.     REGISTER struct link *new;
  814.     REGISTER struct link *head;
  815.  
  816.     head = NULL;
  817.     while (*ctlp != EOS) {
  818.     start = ctlp;
  819.     while (*ctlp != EOS && *ctlp != ',') {
  820.         ctlp++;
  821.     }
  822.     if (*ctlp == ',') {
  823.         *ctlp++ = EOS;
  824.     }
  825.     new = (struct link *) DbugMalloc (sizeof (struct link));
  826.     new -> string = StrDup (start);
  827.     new -> next_link = head;
  828.     head = new;
  829.     }
  830.     return (head);
  831. }
  832.  
  833. /*
  834.  *  FUNCTION
  835.  *
  836.  *    InList      test a given string for member of a given list
  837.  *
  838.  *  SYNOPSIS
  839.  *
  840.  *    LOCAL BOOLEAN InList (linkp, cp)
  841.  *    struct link *linkp;
  842.  *    char *cp;
  843.  *
  844.  *  DESCRIPTION
  845.  *
  846.  *    Tests the string pointed to by "cp" to determine if it is in
  847.  *    the list pointed to by "linkp".  Linkp points to the first
  848.  *    link in the list.  If linkp is NULL then the string is treated
  849.  *    as if it is in the list (I.E all strings are in the null list).
  850.  *    This may seem rather strange at first but leads to the desired
  851.  *    operation if no list is given.    The net effect is that all
  852.  *    strings will be accepted when there is no list, and when there
  853.  *    is a list, only those strings in the list will be accepted.
  854.  *
  855.  */
  856.  
  857. LOCAL BOOLEAN InList (linkp, cp)
  858. struct link *linkp;
  859. char *cp;
  860. {
  861.     REGISTER struct link *scan;
  862.     REGISTER BOOLEAN accept;
  863.  
  864.     if (linkp == NULL) {
  865.     accept = TRUE;
  866.     } else {
  867.     accept = FALSE;
  868.     for (scan = linkp; scan != NULL; scan = scan -> next_link) {
  869.         if (STREQ (scan -> string, cp)) {
  870.         accept = TRUE;
  871.         break;
  872.         }
  873.     }
  874.     }
  875.     return (accept);
  876. }
  877.  
  878. /*
  879.  *  FUNCTION
  880.  *
  881.  *    PushState    push current state onto stack and set up new one
  882.  *
  883.  *  SYNOPSIS
  884.  *
  885.  *    LOCAL VOID PushState ()
  886.  *
  887.  *  DESCRIPTION
  888.  *
  889.  *    Pushes the current state on the state stack, and initializes
  890.  *    a new state.  The only parameter inherited from the previous
  891.  *    state is the function nesting level.  This action can be
  892.  *    inhibited if desired, via the "r" flag.
  893.  *
  894.  *    The state stack is a linked list of states, with the new
  895.  *    state added at the head.  This allows the stack to grow
  896.  *    to the limits of memory if necessary.
  897.  *
  898.  */
  899.  
  900. LOCAL VOID PushState ()
  901. {
  902.     REGISTER struct state *new;
  903.  
  904.     new = (struct state *) DbugMalloc (sizeof (struct state));
  905.     new -> flags = 0;
  906.     new -> delay = 0;
  907.     new -> maxdepth = MAXDEPTH;
  908.     if (stack != NULL) {
  909.     new -> level = stack -> level;
  910.     } else {
  911.     new -> level = 0;
  912.     }
  913.     new -> out_file = stderr;
  914.     new -> functions = NULL;
  915.     new -> p_functions = NULL;
  916.     new -> keywords = NULL;
  917.     new -> processes = NULL;
  918.     new -> next_state = stack;
  919.     stack = new;
  920.     init_done = TRUE;
  921. }
  922.  
  923. /*
  924.  *  FUNCTION
  925.  *
  926.  *    DoTrace    check to see if tracing is current enabled
  927.  *
  928.  *  SYNOPSIS
  929.  *
  930.  *    LOCAL BOOLEAN DoTrace ()
  931.  *
  932.  *  DESCRIPTION
  933.  *
  934.  *    Checks to see if tracing is enabled based on whether the
  935.  *    user has specified tracing, the maximum trace depth has
  936.  *    not yet been reached, the current function is selected,
  937.  *    and the current process is selected.  Returns TRUE if
  938.  *    tracing is enabled, FALSE otherwise.
  939.  *
  940.  */
  941.  
  942. LOCAL BOOLEAN DoTrace ()
  943. {
  944.     REGISTER BOOLEAN trace;
  945.  
  946.     trace = FALSE;
  947.     if (TRACING) {
  948.     if (stack -> level <= stack -> maxdepth) {
  949.         if (InList (stack -> functions, func)) {
  950.         if (InList (stack -> processes, _db_process_)) {
  951.             trace = TRUE;
  952.         }
  953.         }
  954.     }
  955.     }
  956.     return (trace);
  957. }
  958.  
  959. /*
  960.  *  FUNCTION
  961.  *
  962.  *    DoProfile    check to see if profiling is current enabled
  963.  *
  964.  *  SYNOPSIS
  965.  *
  966.  *    LOCAL BOOLEAN DoProfile ()
  967.  *
  968.  *  DESCRIPTION
  969.  *
  970.  *    Checks to see if profiling is enabled based on whether the
  971.  *    user has specified profiling, the maximum trace depth has
  972.  *    not yet been reached, the current function is selected,
  973.  *    and the current process is selected.  Returns TRUE if
  974.  *    profiling is enabled, FALSE otherwise.
  975.  *
  976.  */
  977.  
  978. LOCAL BOOLEAN DoProfile ()
  979. {
  980.     REGISTER BOOLEAN profile;
  981.  
  982.     profile = FALSE;
  983.     if (PROFILING) {
  984.     if (stack -> level <= stack -> maxdepth) {
  985.         if (InList (stack -> p_functions, func)) {
  986.         if (InList (stack -> processes, _db_process_)) {
  987.             profile = TRUE;
  988.         }
  989.         }
  990.     }
  991.     }
  992.     return (profile);
  993. }
  994.  
  995. /*
  996.  *  FUNCTION
  997.  *
  998.  *    _db_keyword_    test keyword for member of keyword list
  999.  *
  1000.  *  SYNOPSIS
  1001.  *
  1002.  *    BOOLEAN _db_keyword_ (keyword)
  1003.  *    char *keyword;
  1004.  *
  1005.  *  DESCRIPTION
  1006.  *
  1007.  *    Test a keyword to determine if it is in the currently active
  1008.  *    keyword list.  As with the function list, a keyword is accepted
  1009.  *    if the list is null, otherwise it must match one of the list
  1010.  *    members.  When debugging is not on, no keywords are accepted.
  1011.  *    After the maximum trace level is exceeded, no keywords are
  1012.  *    accepted (this behavior subject to change).  Additionally,
  1013.  *    the current function and process must be accepted based on
  1014.  *    their respective lists.
  1015.  *
  1016.  *    Returns TRUE if keyword accepted, FALSE otherwise.
  1017.  *
  1018.  */
  1019.  
  1020. BOOLEAN _db_keyword_ (keyword)
  1021. char *keyword;
  1022. {
  1023.     REGISTER BOOLEAN accept;
  1024.  
  1025.     if (!init_done) {
  1026.     _db_push_ ("");
  1027.     }
  1028.     accept = FALSE;
  1029.     if (DEBUGGING) {
  1030.     if (stack -> level <= stack -> maxdepth) {
  1031.         if (InList (stack -> functions, func)) {
  1032.         if (InList (stack -> keywords, keyword)) {
  1033.             if (InList (stack -> processes, _db_process_)) {
  1034.             accept = TRUE;
  1035.             }
  1036.         }
  1037.         }
  1038.     }
  1039.     }
  1040.     return (accept);
  1041. }
  1042.  
  1043. /*
  1044.  *  FUNCTION
  1045.  *
  1046.  *    Indent      indent a line to the given indentation level
  1047.  *
  1048.  *  SYNOPSIS
  1049.  *
  1050.  *    LOCAL VOID Indent (indent)
  1051.  *    int indent;
  1052.  *
  1053.  *  DESCRIPTION
  1054.  *
  1055.  *    Indent a line to the given level.  Note that this is
  1056.  *    a simple minded but portable implementation.
  1057.  *    There are better ways.
  1058.  *
  1059.  *    Also, the indent must be scaled by the compile time option
  1060.  *    of character positions per nesting level.
  1061.  *
  1062.  */
  1063.  
  1064. LOCAL VOID Indent (indent)
  1065. int indent;
  1066. {
  1067.     REGISTER int count;
  1068.     AUTO char buffer[PRINTBUF];
  1069.  
  1070.     indent *= INDENT;
  1071.     for (count = 0; (count < (indent - INDENT)) && (count < (PRINTBUF - 1)); count++) {
  1072.     if ((count % INDENT) == 0) {
  1073.         buffer[count] = '|';
  1074.     } else {
  1075.         buffer[count] = ' ';
  1076.     }
  1077.     }
  1078.     buffer[count] = EOS;
  1079.     (VOID) fprintf (_db_fp_, buffer);
  1080.     (VOID) fflush (_db_fp_);
  1081. }
  1082.  
  1083. /*
  1084.  *  FUNCTION
  1085.  *
  1086.  *    FreeList    free all memory associated with a linked list
  1087.  *
  1088.  *  SYNOPSIS
  1089.  *
  1090.  *    LOCAL VOID FreeList (linkp)
  1091.  *    struct link *linkp;
  1092.  *
  1093.  *  DESCRIPTION
  1094.  *
  1095.  *    Given pointer to the head of a linked list, frees all
  1096.  *    memory held by the list and the members of the list.
  1097.  *
  1098.  */
  1099.  
  1100. LOCAL VOID FreeList (linkp)
  1101. struct link *linkp;
  1102. {
  1103.     REGISTER struct link *old;
  1104.  
  1105.     while (linkp != NULL) {
  1106.     old = linkp;
  1107.     linkp = linkp -> next_link;
  1108.     if (old -> string != NULL) {
  1109.         free (old -> string);
  1110.     }
  1111.     free ((char *) old);
  1112.     }
  1113. }
  1114.  
  1115. /*
  1116.  *  FUNCTION
  1117.  *
  1118.  *    StrDup     make a duplicate of a string in new memory
  1119.  *
  1120.  *  SYNOPSIS
  1121.  *
  1122.  *    LOCAL char *StrDup (string)
  1123.  *    char *string;
  1124.  *
  1125.  *  DESCRIPTION
  1126.  *
  1127.  *    Given pointer to a string, allocates sufficient memory to make
  1128.  *    a duplicate copy, and copies the string to the newly allocated
  1129.  *    memory.  Failure to allocated sufficient memory is immediately
  1130.  *    fatal.
  1131.  *
  1132.  */
  1133.  
  1134. LOCAL char *StrDup (string)
  1135. char *string;
  1136. {
  1137.     REGISTER char *new;
  1138.  
  1139.     new = (char *) DbugMalloc (strlen (string) + 1);
  1140.     (VOID) strcpy (new, string);
  1141.     return (new);
  1142. }
  1143.  
  1144. /*
  1145.  *  FUNCTION
  1146.  *
  1147.  *    DoPrefix    print debugger line prefix prior to indentation
  1148.  *
  1149.  *  SYNOPSIS
  1150.  *
  1151.  *    LOCAL VOID DoPrefix (_line_)
  1152.  *    int _line_;
  1153.  *
  1154.  *  DESCRIPTION
  1155.  *
  1156.  *    Print prefix common to all debugger output lines, prior to
  1157.  *    doing indentation if necessary.  Print such information as
  1158.  *    current process name, current source file name and line number,
  1159.  *    and current function nesting depth.
  1160.  *
  1161.  */
  1162.  
  1163.  
  1164. LOCAL VOID DoPrefix (_line_)
  1165. int _line_;
  1166. {
  1167. #if (unix || xenix)
  1168.     extern int getpid ();
  1169. #endif
  1170.  
  1171.     lineno++;
  1172. #if (unix || xenix)
  1173.     if (stack -> flags & PID_ON) {
  1174.     (VOID) fprintf (_db_fp_, "%5d: ", getpid ());
  1175.     }
  1176. #endif
  1177.     if (stack -> flags & NUMBER_ON) {
  1178.     (VOID) fprintf (_db_fp_, "%5d: ", lineno);
  1179.     }
  1180.     if (stack -> flags & PROCESS_ON) {
  1181.     (VOID) fprintf (_db_fp_, "%s: ", _db_process_);
  1182.     }
  1183.     if (stack -> flags & FILE_ON) {
  1184.     (VOID) fprintf (_db_fp_, "%14s: ", file);
  1185.     }
  1186.     if (stack -> flags & LINE_ON) {
  1187.     (VOID) fprintf (_db_fp_, "%5d: ", _line_);
  1188.     }
  1189.     if (stack -> flags & DEPTH_ON) {
  1190.     (VOID) fprintf (_db_fp_, "%4d: ", stack -> level);
  1191.     }
  1192.     (VOID) fflush (_db_fp_);
  1193. }
  1194.  
  1195. /*
  1196.  *  FUNCTION
  1197.  *
  1198.  *    OpenFile    open new output stream for debugger output
  1199.  *
  1200.  *  SYNOPSIS
  1201.  *
  1202.  *    LOCAL VOID OpenFile (name)
  1203.  *    char *name;
  1204.  *
  1205.  *  DESCRIPTION
  1206.  *
  1207.  *    Given name of a new file (or "-" for stdout) opens the file
  1208.  *    and sets the output stream to the new file.
  1209.  *
  1210.  */
  1211.  
  1212. LOCAL VOID OpenFile (name)
  1213. char *name;
  1214. {
  1215.     REGISTER FILE *fp;
  1216.     REGISTER BOOLEAN newfile;
  1217.  
  1218.     if (name != NULL) {
  1219.     if (strcmp (name, "-") == 0) {
  1220.         _db_fp_ = stdout;
  1221.         stack -> out_file = _db_fp_;
  1222.     } else {
  1223.         if (!Writable (name)) {
  1224.         (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1225.         perror ("");
  1226.         (VOID) fflush (_db_fp_);
  1227.         (VOID) Delay (stack -> delay);
  1228.         } else {
  1229.         if (EXISTS (name)) {
  1230.             newfile = FALSE;
  1231.         } else {
  1232.             newfile = TRUE;
  1233.         }
  1234.         fp = fopen (name, "a");
  1235.         if (fp == NULL) {
  1236.             (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1237.             perror ("");
  1238.             (VOID) fflush (_db_fp_);
  1239.             (VOID) Delay (stack -> delay);
  1240.         } else {
  1241.             _db_fp_ = fp;
  1242.             stack -> out_file = fp;
  1243.             if (newfile) {
  1244.             ChangeOwner (name);
  1245.             }
  1246.         }
  1247.         }
  1248.     }
  1249.     }
  1250. }
  1251.  
  1252. /*
  1253.  *  FUNCTION
  1254.  *
  1255.  *    OpenProfile    open new output stream for profiler output
  1256.  *
  1257.  *  SYNOPSIS
  1258.  *
  1259.  *    LOCAL VOID OpenProfile (name)
  1260.  *    char *name;
  1261.  *
  1262.  *  DESCRIPTION
  1263.  *
  1264.  *    Given name of a new file, opens the file
  1265.  *    and sets the profiler output stream to the new file.
  1266.  *
  1267.  *    It is currently unclear whether the prefered behavior is
  1268.  *    to truncate any existing file, or simply append to it.
  1269.  *    The latter behavior would be desirable for collecting
  1270.  *    accumulated runtime history over a number of separate
  1271.  *    runs.  It might take some changes to the analyzer program
  1272.  *    though, and the notes that Binayak sent with the profiling
  1273.  *    diffs indicated that append was the normal mode, but this
  1274.  *    does not appear to agree with the actual code. I haven't
  1275.  *    investigated at this time [fnf; 24-Jul-87].
  1276.  */
  1277.  
  1278. LOCAL VOID OpenProfile (name)
  1279. char *name;
  1280. {
  1281.     REGISTER FILE *fp;
  1282.     REGISTER BOOLEAN newfile;
  1283.  
  1284.     if (name != NULL) {
  1285.     if (!Writable (name)) {
  1286.         (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1287.         perror ("");
  1288.         (VOID) fflush (_db_fp_);
  1289.         (VOID) Delay (stack -> delay);
  1290.     } else {
  1291.         if (EXISTS (name)) {
  1292.         newfile = FALSE;
  1293.         } else {
  1294.         newfile = TRUE;
  1295.         }
  1296.         fp = fopen (name, "w");
  1297.         if (fp == NULL) {
  1298.         (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1299.         perror ("");
  1300.         (VOID) fflush (_db_fp_);
  1301.         (VOID) Delay (stack -> delay);
  1302.         } else {
  1303.         _db_pfp_ = fp;
  1304.         stack -> prof_file = fp;
  1305.         if (newfile) {
  1306.             ChangeOwner (name);
  1307.         }
  1308.         }
  1309.     }
  1310.     }
  1311. }
  1312.  
  1313. /*
  1314.  *  FUNCTION
  1315.  *
  1316.  *    CloseFile    close the debug output stream
  1317.  *
  1318.  *  SYNOPSIS
  1319.  *
  1320.  *    LOCAL VOID CloseFile (fp)
  1321.  *    FILE *fp;
  1322.  *
  1323.  *  DESCRIPTION
  1324.  *
  1325.  *    Closes the debug output stream unless it is standard output
  1326.  *    or standard error.
  1327.  *
  1328.  */
  1329.  
  1330. LOCAL VOID CloseFile (fp)
  1331. FILE *fp;
  1332. {
  1333.     if (fp != stderr && fp != stdout) {
  1334.     if (fclose (fp) == EOF) {
  1335.         (VOID) fprintf (stderr, ERR_CLOSE, _db_process_);
  1336.         perror ("");
  1337.         (VOID) fflush (stderr);
  1338.         (VOID) Delay (stack -> delay);
  1339.     }
  1340.     }
  1341. }
  1342.  
  1343. /*
  1344.  *  FUNCTION
  1345.  *
  1346.  *    DbugExit    print error message and exit
  1347.  *
  1348.  *  SYNOPSIS
  1349.  *
  1350.  *    LOCAL VOID DbugExit (why)
  1351.  *    char *why;
  1352.  *
  1353.  *  DESCRIPTION
  1354.  *
  1355.  *    Prints error message using current process name, the reason for
  1356.  *    aborting (typically out of memory), and exits with status 1.
  1357.  *    This should probably be changed to use a status code
  1358.  *    defined in the user's debugger include file.
  1359.  *
  1360.  */
  1361.  
  1362. LOCAL VOID DbugExit (why)
  1363. char *why;
  1364. {
  1365.     (VOID) fprintf (stderr, ERR_ABORT, _db_process_, why);
  1366.     (VOID) fflush (stderr);
  1367.     (VOID) Delay (stack -> delay);
  1368.     exit (1);
  1369. }
  1370.  
  1371. /*
  1372.  *  FUNCTION
  1373.  *
  1374.  *    DbugMalloc    allocate memory for debugger runtime support
  1375.  *
  1376.  *  SYNOPSIS
  1377.  *
  1378.  *    LOCAL long *DbugMalloc (size)
  1379.  *    int size;
  1380.  *
  1381.  *  DESCRIPTION
  1382.  *
  1383.  *    Allocate more memory for debugger runtime support functions.
  1384.  *    Failure to to allocate the requested number of bytes is
  1385.  *    immediately fatal to the current process.  This may be
  1386.  *    rather unfriendly behavior.  It might be better to simply
  1387.  *    print a warning message, freeze the current debugger state,
  1388.  *    and continue execution.
  1389.  *
  1390.  */
  1391.  
  1392. LOCAL long *DbugMalloc (size)
  1393. int size;
  1394. {
  1395.     register long *new;
  1396.  
  1397.     new = (long *) malloc ((unsigned int) size);
  1398.     if (new == NULL) {
  1399.     DbugExit ("out of memory");
  1400.     }
  1401.     return (new);
  1402. }
  1403.  
  1404. /*
  1405.  *    This function may be eliminated when strtok is available
  1406.  *    in the runtime environment (missing from BSD4.1).
  1407.  */
  1408.  
  1409. LOCAL char *strtok (s1, s2)
  1410. char *s1, *s2;
  1411. {
  1412.     static char *end = NULL;
  1413.     REGISTER char *rtnval;
  1414.  
  1415.     rtnval = NULL;
  1416.     if (s2 != NULL) {
  1417.     if (s1 != NULL) {
  1418.         end = s1;
  1419.         rtnval = strtok ((char *) NULL, s2);
  1420.     } else if (end != NULL) {
  1421.         if (*end != EOS) {
  1422.         rtnval = end;
  1423.         while (*end != *s2 && *end != EOS) {end++;}
  1424.         if (*end != EOS) {
  1425.             *end++ = EOS;
  1426.         }
  1427.         }
  1428.     }
  1429.     }
  1430.     return (rtnval);
  1431. }
  1432.  
  1433. /*
  1434.  *  FUNCTION
  1435.  *
  1436.  *    BaseName    strip leading pathname components from name
  1437.  *
  1438.  *  SYNOPSIS
  1439.  *
  1440.  *    LOCAL char *BaseName (pathname)
  1441.  *    char *pathname;
  1442.  *
  1443.  *  DESCRIPTION
  1444.  *
  1445.  *    Given pointer to a complete pathname, locates the base file
  1446.  *    name at the end of the pathname and returns a pointer to
  1447.  *    it.
  1448.  *
  1449.  */
  1450.  
  1451. LOCAL char *BaseName (pathname)
  1452. char *pathname;
  1453. {
  1454.     register char *base;
  1455.  
  1456.     base = strrchr (pathname, '/');
  1457.     if (base++ == NULL) {
  1458.     base = pathname;
  1459.     }
  1460.     return (base);
  1461. }
  1462.  
  1463. /*
  1464.  *  FUNCTION
  1465.  *
  1466.  *    Writable    test to see if a pathname is writable/creatable
  1467.  *
  1468.  *  SYNOPSIS
  1469.  *
  1470.  *    LOCAL BOOLEAN Writable (pathname)
  1471.  *    char *pathname;
  1472.  *
  1473.  *  DESCRIPTION
  1474.  *
  1475.  *    Because the debugger might be linked in with a program that
  1476.  *    runs with the set-uid-bit (suid) set, we have to be careful
  1477.  *    about opening a user named file for debug output.  This consists
  1478.  *    of checking the file for write access with the real user id,
  1479.  *    or checking the directory where the file will be created.
  1480.  *
  1481.  *    Returns TRUE if the user would normally be allowed write or
  1482.  *    create access to the named file.  Returns FALSE otherwise.
  1483.  *
  1484.  */
  1485.  
  1486. LOCAL BOOLEAN Writable (pathname)
  1487. char *pathname;
  1488. {
  1489.     REGISTER BOOLEAN granted;
  1490. #if (unix || xenix)
  1491.     REGISTER char *lastslash;
  1492. #endif
  1493.  
  1494. #if (!unix && !xenix)
  1495.     granted = TRUE;
  1496. #else
  1497.     granted = FALSE;
  1498.     if (EXISTS (pathname)) {
  1499.     if (WRITABLE (pathname)) {
  1500.         granted = TRUE;
  1501.     }
  1502.     } else {
  1503.     lastslash = strrchr (pathname, '/');
  1504.     if (lastslash != NULL) {
  1505.         *lastslash = EOS;
  1506.     } else {
  1507.         pathname = ".";
  1508.     }
  1509.     if (WRITABLE (pathname)) {
  1510.         granted = TRUE;
  1511.     }
  1512.     if (lastslash != NULL) {
  1513.         *lastslash = '/';
  1514.     }
  1515.     }
  1516. #endif
  1517.     return (granted);
  1518. }
  1519.  
  1520. /*
  1521.  *    This function may be eliminated when strrchr is available
  1522.  *    in the runtime environment (missing from BSD4.1).
  1523.  *    Alternately, you can use rindex() on BSD systems.
  1524.  */
  1525.  
  1526. LOCAL char *strrchr (s, c)
  1527. char *s;
  1528. char c;
  1529. {
  1530.     REGISTER char *scan;
  1531.  
  1532.     for (scan = s; *scan != EOS; scan++) {;}
  1533.     while (scan > s && *--scan != c) {;}
  1534.     if (*scan != c) {
  1535.     scan = NULL;
  1536.     }
  1537.     return (scan);
  1538. }
  1539.  
  1540. /*
  1541.  *  FUNCTION
  1542.  *
  1543.  *    ChangeOwner    change owner to real user for suid programs
  1544.  *
  1545.  *  SYNOPSIS
  1546.  *
  1547.  *    LOCAL VOID ChangeOwner (pathname)
  1548.  *
  1549.  *  DESCRIPTION
  1550.  *
  1551.  *    For unix systems, change the owner of the newly created debug
  1552.  *    file to the real owner.  This is strictly for the benefit of
  1553.  *    programs that are running with the set-user-id bit set.
  1554.  *
  1555.  *    Note that at this point, the fact that pathname represents
  1556.  *    a newly created file has already been established.  If the
  1557.  *    program that the debugger is linked to is not running with
  1558.  *    the suid bit set, then this operation is redundant (but
  1559.  *    harmless).
  1560.  *
  1561.  */
  1562.  
  1563. LOCAL VOID ChangeOwner (pathname)
  1564. char *pathname;
  1565. {
  1566. #if (unix || xenix)
  1567.     if (chown (pathname, getuid (), getgid ()) == -1) {
  1568.     (VOID) fprintf (stderr, ERR_CHOWN, _db_process_, pathname);
  1569.     perror ("");
  1570.     (VOID) fflush (stderr);
  1571.     (VOID) Delay (stack -> delay);
  1572.     }
  1573. #endif
  1574. }
  1575.  
  1576. /*
  1577.  *  FUNCTION
  1578.  *
  1579.  *    _db_setjmp_    save debugger environment
  1580.  *
  1581.  *  SYNOPSIS
  1582.  *
  1583.  *    VOID _db_setjmp_ ()
  1584.  *
  1585.  *  DESCRIPTION
  1586.  *
  1587.  *    Invoked as part of the user's DBUG_SETJMP macro to save
  1588.  *    the debugger environment in parallel with saving the user's
  1589.  *    environment.
  1590.  *
  1591.  */
  1592.  
  1593. VOID _db_setjmp_ ()
  1594. {
  1595.    jmplevel = stack -> level;
  1596.    jmpfunc = func;
  1597.    jmpfile = file;
  1598. }
  1599.  
  1600. /*
  1601.  *  FUNCTION
  1602.  *
  1603.  *    _db_longjmp_    restore previously saved debugger environment
  1604.  *
  1605.  *  SYNOPSIS
  1606.  *
  1607.  *    VOID _db_longjmp_ ()
  1608.  *
  1609.  *  DESCRIPTION
  1610.  *
  1611.  *    Invoked as part of the user's DBUG_LONGJMP macro to restore
  1612.  *    the debugger environment in parallel with restoring the user's
  1613.  *    previously saved environment.
  1614.  *
  1615.  */
  1616.  
  1617. VOID _db_longjmp_ ()
  1618. {
  1619.     stack -> level = jmplevel;
  1620.     if (jmpfunc) {
  1621.     func = jmpfunc;
  1622.     }
  1623.     if (jmpfile) {
  1624.     file = jmpfile;
  1625.     }
  1626. }
  1627.  
  1628. /*
  1629.  *  FUNCTION
  1630.  *
  1631.  *    DelayArg   convert D flag argument to appropriate value
  1632.  *
  1633.  *  SYNOPSIS
  1634.  *
  1635.  *    LOCAL int DelayArg (value)
  1636.  *    int value;
  1637.  *
  1638.  *  DESCRIPTION
  1639.  *
  1640.  *    Converts delay argument, given in tenths of a second, to the
  1641.  *    appropriate numerical argument used by the system to delay
  1642.  *    that that many tenths of a second.  For example, on the
  1643.  *    AMIGA, there is a system call "Delay()" which takes an
  1644.  *    argument in ticks (50 per second).  On unix, the sleep
  1645.  *    command takes seconds.    Thus a value of "10", for one
  1646.  *    second of delay, gets converted to 50 on the AMIGA, and 1
  1647.  *    on unix.  Other systems will need to use a timing loop.
  1648.  *
  1649.  */
  1650.  
  1651. LOCAL int DelayArg (value)
  1652. int value;
  1653. {
  1654.     unsigned int delayarg = 0;
  1655.  
  1656. #if (unix || xenix)
  1657.     delayarg = value / 10;        /* Delay is in seconds for sleep () */
  1658. #endif
  1659. #if AMIGA
  1660.     delayarg = (HZ * value) / 10;       /* Delay in ticks for Delay () */
  1661. #endif
  1662.     return ((int) delayarg);
  1663. }
  1664.  
  1665. /*
  1666.  *    A dummy delay stub for systems that do not support delays.
  1667.  *    With a little work, this can be turned into a timing loop.
  1668.  */
  1669.  
  1670. #if (!unix && !xenix && !AMIGA)
  1671. Delay ()
  1672. {
  1673. }
  1674. #endif
  1675.  
  1676. /*
  1677.  *  FUNCTION
  1678.  *
  1679.  *    perror      perror simulation for systems that don't have it
  1680.  *
  1681.  *  SYNOPSIS
  1682.  *
  1683.  *    LOCAL VOID perror (s)
  1684.  *    char *s;
  1685.  *
  1686.  *  DESCRIPTION
  1687.  *
  1688.  *    Perror produces a message on the standard error stream which
  1689.  *    provides more information about the library or system error
  1690.  *    just encountered.  The argument string s is printed, followed
  1691.  *    by a ':', a blank, and then a message and a newline.
  1692.  *
  1693.  *    An undocumented feature of the unix perror is that if the string
  1694.  *    's' is a null string (NOT a NULL pointer!), then the ':' and
  1695.  *    blank are not printed.
  1696.  *
  1697.  *    This version just complains about an "unknown system error".
  1698.  *
  1699.  */
  1700.  
  1701. #if !unix && !xenix && !(AMIGA && (LATTICE || _DCC))
  1702.  
  1703. LOCAL VOID perror (s)
  1704. char *s;
  1705. {
  1706.     if (s && *s != EOS) {
  1707.     (VOID) fprintf (stderr, "%s: ", s);
  1708.     }
  1709.     (VOID) fprintf (stderr, "<unknown system error>\n");
  1710. }
  1711.  
  1712. #endif    /* !unix && !xenix && !(AMIGA && (LATTICE || _DCC)) */
  1713.  
  1714. /*
  1715.  * Here we need the definitions of the clock routine.  Add your
  1716.  * own for whatever system that you have.
  1717.  */
  1718.  
  1719. #if (unix || xenix)
  1720.  
  1721. # include <sys/param.h>
  1722. # if BSD4_3 || sun
  1723.  
  1724. /*
  1725.  * Definition of the Clock() routine for 4.3 BSD.
  1726.  */
  1727.  
  1728. #include <sys/time.h>
  1729. #include <sys/resource.h>
  1730.  
  1731. /*
  1732.  * Returns the user time in milliseconds used by this process so
  1733.  * far.
  1734.  */
  1735.  
  1736. LOCAL unsigned long Clock ()
  1737. {
  1738.     struct rusage ru;
  1739.  
  1740.     (VOID) getrusage (RUSAGE_SELF, &ru);
  1741.     return ((ru.ru_utime.tv_sec * 1000) + (ru.ru_utime.tv_usec / 1000));
  1742. }
  1743.  
  1744. #else
  1745.  
  1746. LOCAL unsigned long Clock ()
  1747. {
  1748.     return (0);
  1749. }
  1750.  
  1751. # endif
  1752.  
  1753. #else
  1754.  
  1755. #if AMIGA
  1756.  
  1757. struct DateStamp {        /* Yes, this is a hack, but doing it right */
  1758.     long ds_Days;        /* is incredibly ugly without splitting this */
  1759.     long ds_Minute;     /* off into a separate file */
  1760.     long ds_Tick;
  1761. };
  1762.  
  1763. static int first_clock = TRUE;
  1764. static struct DateStamp begin;
  1765. static struct DateStamp elapsed;
  1766.  
  1767. LOCAL unsigned long Clock ()
  1768. {
  1769.     register struct DateStamp *now;
  1770.     register unsigned long millisec = 0;
  1771.     extern VOID *AllocMem ();
  1772.  
  1773.     now = (struct DateStamp *) AllocMem ((long) sizeof (struct DateStamp), 0L);
  1774.     if (now != NULL) {
  1775.     if (first_clock == TRUE) {
  1776.         first_clock = FALSE;
  1777.         (VOID) DateStamp (now);
  1778.         begin = *now;
  1779.     }
  1780.     (VOID) DateStamp (now);
  1781.     millisec = 24 * 3600 * (1000 / HZ) * (now -> ds_Days - begin.ds_Days);
  1782.     millisec += 60 * (1000 / HZ) * (now -> ds_Minute - begin.ds_Minute);
  1783.     millisec += (1000 / HZ) * (now -> ds_Tick - begin.ds_Tick);
  1784.     (VOID) FreeMem (now, (long) sizeof (struct DateStamp));
  1785.     }
  1786.     return (millisec);
  1787. }
  1788.  
  1789. #endif    /* AMIGA */
  1790. #endif    /* unix */
  1791.  
  1792. #if NO_VARARGS
  1793.  
  1794. /*
  1795.  *    Fake vfprintf for systems that don't support it.  If this
  1796.  *    doesn't work, you are probably SOL...
  1797.  */
  1798.  
  1799. static int vfprintf (stream, format, ap)
  1800. FILE *stream;
  1801. char *format;
  1802. va_list ap;
  1803. {
  1804.     int rtnval;
  1805.     extern int fprintf ();
  1806.     ARGS_DCL;
  1807.  
  1808.     ARG0 =  va_arg (ap, ARGS_TYPE);
  1809.     ARG1 =  va_arg (ap, ARGS_TYPE);
  1810.     ARG2 =  va_arg (ap, ARGS_TYPE);
  1811.     ARG3 =  va_arg (ap, ARGS_TYPE);
  1812.     ARG4 =  va_arg (ap, ARGS_TYPE);
  1813.     ARG5 =  va_arg (ap, ARGS_TYPE);
  1814.     ARG6 =  va_arg (ap, ARGS_TYPE);
  1815.     ARG7 =  va_arg (ap, ARGS_TYPE);
  1816.     ARG8 =  va_arg (ap, ARGS_TYPE);
  1817.     ARG9 =  va_arg (ap, ARGS_TYPE);
  1818.     rtnval = fprintf (stream, format, ARGS_LIST);
  1819.     return (rtnval);
  1820. }
  1821.  
  1822. #endif    /* NO_VARARGS */
  1823.